<?php

	/**
	 * Directory
	 *
	 * Exposes static methods for creating, moving, and enumerating through directories and subdirectories.
	 * This class cannot be inherited
	 *
	 * @package       Core
	 * @subpackage    IO
     *
     * @license      GNU Lesser General Public Licence see LICENCE-LGPL file or http://www.gnu.org/licenses/lgpl.html
	 */
	final class Core_IO_Directory
	{

	////////////////////////////////////////////////////////////////////////////
	//                             Static Methods                             //
	////////////////////////////////////////////////////////////////////////////

		/**
		 * Determines whether the given path refers to an existing directory on disk
		 *
		 * @param		string					$path : The path to test
		 * @return		boolean					true if path refers to an existing directory, otherwise, false
		 */
		public static function exists($path)
		{
			return (@is_dir($path) && @file_exists($path)) ? true: false;
		}

		/**
		 * Creates all directories and subdirectories as specified by path
		 *
		 * @param		string					$path : The directory path to create
		 * @param		integer					$mode : The octal rights for the directory (ignored on Windows)
		 * @throws		Core_IO_Exception
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
		 * @return		void
		 */
		public static function createDirectory($path, $mode = 0777)
		{
            if ($path === null) {
				throw new Core_ArgumentNullException('path is a null reference');
			}

            if (strlen(trim($path)) == 0) {
				throw new Core_ArgumentException('path is a zero-length string');
			}

			Core_IO_Path::checkInvalidPathChars($path);

            if (!@mkdir($path, $mode, true)) {
				throw new Core_IO_Exception('Unable to create the directory specified by path');
			}
		}

		/**
		 * Deletes the specified directory and, if indicated, any subdirectories in the directory
		 *
		 * @param		string					$path : The name of the directory to remove
		 * @param		boolean					[$recursive] : true to remove directories, subdirectories, and files in path, otherwise, false
		 * @throws		Core_IO_Exception
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
		 * @throws		Core_IO_DirectoryNotFoundException
		 * @return		void
		 */
		public static function delete($path, $recursive = false)
		{
            if ($path === null) {
				throw new Core_ArgumentNullException('path is a null reference');
			}

            if (strlen(trim($path)) == 0) {
				throw new Core_ArgumentException('path is a zero-length string');
			}

			Core_IO_Path::checkInvalidPathChars($path);

			if (!self::exists($path)) {
				throw new Core_IO_DirectoryNotFoundException('The specified path is invalid');
			}

			if ($recursive) {
				$directory = (substr($path, strlen($path) - 1) == DIRECTORY_SEPARATOR) ? $path . DIRECTORY_SEPARATOR : $path;

				$handle = @opendir($directory);
				while ($file = @readdir($handle)) {
					if (!@in_array($file, array('.', '..'))) {
						if (!@is_dir($directory . $file)) {
							Core_IO_File::delete($directory . $file);
						} else {
							self::delete($directory . $file, true);
						}
					}
				}

				@closedir($handle);

				if (!@rmdir($directory)) {
					throw new Core_IO_Exception('Unable to delete the directory specified by path');
				}
			} else if (!@rmdir($path)) {
				throw new Core_IO_Exception('The directory specified by path is read-only, or recursive is false and path is not an empty directory');
			}
		}

		/**
		 * Moves a file or a directory and its contents to a new location
		 *
		 * @param		string					$sourceDirName : The path of the file or directory to move
		 * @param		string					$destDirName : The path to the new location for sourceDirName
		 * @throws		Core_IO_Exception
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
		 * @throws		Core_IO_DirectoryNotFoundException
		 * @return		void
		 */
		public static function move($sourceDirName, $destDirName)
		{
            if ($sourceDirName === null) {
				throw new Core_ArgumentNullException('sourceDirName is a null reference');
			}

            if ($destDirName === null) {
				throw new Core_ArgumentNullException('destDirName is a null reference');
			}

            if (strlen(trim($sourceDirName)) == 0) {
				throw new Core_ArgumentException('sourceDirName is a zero-length string');
			}

            if (strlen(trim($destDirName)) == 0) {
				throw new Core_ArgumentException('destDirName is a zero-length string');
			}

			Core_IO_Path::checkInvalidPathChars($sourceDirName);
			Core_IO_Path::checkInvalidPathChars($destDirName);

			if (!self::exists($sourceDirName)) {
				throw new Core_IO_DirectoryNotFoundException('The path specified by sourceDirName is invalid');
			}

			if (self::exists($destDirName)) {
				throw new Core_IO_Exception('destDirName already exists');
			}

			if ($sourceDirName == $destDirName) {
				throw new Core_IO_Exception('The sourceDirName and destDirName parameters refer to the same file or directory');
			}

			@rename($sourceDirName, $destDirName);
		}

		/**
		 * Gets the names of subdirectories in the specified directory
		 *
		 * @param		string					$path : The path for which an array of subdirectory names is returned
		 * @param		boolean					$fullpath : should the subdirectories be named with fullpath
		 * @throws		Core_IO_Exception
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
		 * @throws		Core_IO_DirectoryNotFoundException
		 * @return		string[]                    An array of type string containing the names of subdirectories in path
		 */
		public static function getDirectories($path, $fullpath = false)
		{
            if ($path === null) {
				throw new Core_ArgumentNullException('path is a null reference');
			}

            if (strlen(trim($path)) == 0) {
				throw new Core_ArgumentException('path is a zero-length string');
			}

			Core_IO_Path::checkInvalidPathChars($path);

			if (!self::exists($path)) {
				throw new Core_IO_DirectoryNotFoundException('The specified path is invalid');
			}

			if (@Core_IO_File::exists($path) && !@is_dir($path)) {
				throw new Core_IO_Exception('path is a file name');
			}

			$subDirectories = array();
			$handle = @opendir($path);
			while ($file = @readdir($handle)) {
				if (!in_array($file, array('.', '..', '.svn'))) {
					if (@is_dir(Core_IO_Path::combine($path, $file))) {
						$subDirectories[] = ($fullpath) ? Core_IO_Path::combine($path, $file) : $file;
					}
				}
			}

			@closedir($handle);

			return $subDirectories;
		}

		/**
		 * Returns the names of files in the specified directory
		 *
		 * @param		string			        $path : The path for which an array of subdirectory names is returned
		 * @param		boolean					$fullpath : should the files be named with fullpath
		 * @param		boolean					$recursive : get recursivly files of the directory
		 * @param		array					$extensions : get specified extentions
		 * @throws		Core_IO_Exception
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
		 * @throws		Core_IO_DirectoryNotFoundException
		 * @return		string[]					A string array of file names in the specified directory
		 */
		public static function getFiles($path, $fullpath = true, $recursive = false, $extensions = array())
		{
            if ($path === null) {
				throw new Core_ArgumentNullException('path is a null reference');
			}

            if (strlen(trim($path)) == 0) {
				throw new Core_ArgumentException('path is a zero-length string');
			}

			Core_IO_Path::checkInvalidPathChars($path);

			if (!self::exists($path)) {
				throw new Core_IO_DirectoryNotFoundException('The specified path is invalid');
			}

			if (@Core_IO_File::exists($path) && !@is_dir($path)) {
				throw new Core_IO_Exception('path is a file name');
			}

			$files           = array();
			$recursiveDirs   = array();
			$handle = @opendir($path);
			while ($file = @readdir($handle)) {
				if (!in_array($file, array('.', '..', '.svn'))) {
                   	$tmpFile = Core_IO_Path::combine($path, $file);
					if (!@is_dir($tmpFile)) {
	                    $currentExtension   = substr($file, strpos($file, '.'));
	                    if (count($extensions) == 0 || in_array($currentExtension, $extensions)) {
								$files[] = ($fullpath) ? realpath($tmpFile) : $file;
	                    }
					} elseif ($recursive == true) {
						$recursiveDirs[]   = $tmpFile;
					}
				}
			}
			
			if ($recursive == true) {
				foreach ($recursiveDirs as $dir) {
				    $files   = array_merge($files, self::getFiles($dir, $fullpath, $recursive, $extensions));
				}
			}

			@closedir($handle);

			return $files;
		}
	}
